<?php
/**
 * 1.信号会打断sleep
 * 2.curl http://localhost:8080/?s=2      s表示sleep的秒数
 event里信号会被阻塞，必须用event来注册信号处理

<<<<<<< HEAD
pcntl_signal与Event::signal处理信号的区别？
 *
 *
=======
>>>>>>> 4413f48eaf63ea26b1099125657a2e86787d2f19
 */
define('HOST','0.0.0.0');
define('PORT',8080);
pcntl_async_signals(true);

pcntl_signal(SIGUSR1,'sigfun',false);   //这个信号在event里有处理过，能及时响应
pcntl_signal(SIGUSR2,'sigfun',false);   //这个信号在event里没处理，只有当event有事件处理时 才会响应信号

echo "#######".posix_getpid().'##########'.PHP_EOL;

cli_set_process_title("hk_parent:".posix_getpid());

$work_num=1;

$workers=[];

for($i=0;$i<$work_num;$i++){
    $pid=pcntl_fork();
    if($pid<0) exit("fork err");
    if($pid==0){
        //子进程
        workerFun();
        exit();
    }else{
        $workers[]=$pid;
    }
}

while(1){
    //回收子进程
    $pid=pcntl_wait($status,WUNTRACED);
    $errno=pcntl_get_last_error();
    //No child processes
    if($errno ==10){
        break;
    }
    echo $errno."-------------------------------------------".pcntl_strerror($errno).PHP_EOL;

    if($pid>0){
        echo $pid." exited!".PHP_EOL;
    }
}

function workerFun(){
    $pid=posix_getpid();
    $eventConfig=new EventConfig();
    $eventBase=new EventBase($eventConfig);
    cli_set_process_title("hk_son:".$pid);


    #\pcntl_signal(SIGUSR1, SIG_IGN, false);
    $signalEvent=Event::signal($eventBase,SIGUSR1,function ($signo){
        echo " ".posix_getpid()." event-signo:".$signo.PHP_EOL;

    });
    $signalEvent->add();

    $host =HOST;
    $port = PORT;
    $listen_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    socket_set_option($listen_socket, SOL_SOCKET, SO_REUSEPORT, 1);
    socket_bind($listen_socket, $host, $port);
    socket_listen($listen_socket);

    socket_set_nonblock($listen_socket);

    echo '[pid='.$pid."] http server on:http://{$host}:{$port}" . PHP_EOL;

    $event = new Event($eventBase, $listen_socket, Event::READ | Event::PERSIST,
        function ($listen_socket)use($pid) {
            #pcntl_sigprocmask( SIG_BLOCK, array(SIGUSR1 ),$a_oldset );
            if (($connect_socket = socket_accept($listen_socket)) != false) {
                $connect_socket_num=intval($connect_socket);
                echo "new connect " . $connect_socket_num . PHP_EOL;
                E:
                $r=socket_recv($connect_socket,$buf,2048,MSG_DONTWAIT);
                if($r!==false){
                    list($_GET, $_POST, $_COOKIE, $_SERVER) = parse_http($buf);
                    $s=$_GET['s']??1;
                    $s=intval($s);
                    #echo $pid."-before sleep-".$connect_socket_num."===".date("H:i:s").PHP_EOL;
                    #sleep($s);
                    #echo $pid."-after  sleep-". $connect_socket_num."===".date("H:i:s").PHP_EOL;
                }else{
                    $errno=socket_last_error();
                    if($errno==11){
                        goto E;
                    }
                    echo $errno.":".socket_strerror($errno).PHP_EOL;
                }
                $content="Hi ".date("Y-m-d H:i:s").PHP_EOL;
                $msg = "HTTP/1.0 200 OK\r\nContent-Length:".strlen($content)."\r\n\r\n".$content;
                socket_write($connect_socket, $msg, strlen($msg));
                socket_close($connect_socket);
              # pcntl_sigprocmask( SIG_UNBLOCK, array(SIGUSR1 ),$a_oldset );

            }
        }, $listen_socket);

    $event->add();

    $eventBase->loop();

}
function sigfun($signo){
    echo " ".posix_getpid()." get signum: ".$signo."----".date("H:i:s").PHP_EOL;
}

function parse_http($http)
{
    // 初始化
    $_POST = $_GET = $_COOKIE = $_REQUEST = $_SESSION  =  array();
    $GLOBALS['HTTP_RAW_POST_DATA'] = '';
    // 需要设置的变量名
    $_SERVER = array (
        'QUERY_STRING' => '',
        'REQUEST_METHOD' => '',
        'REQUEST_URI' => '',
        'SERVER_PROTOCOL' => '',
        'SERVER_SOFTWARE' => '',
        'SERVER_NAME' => '',
        'HTTP_HOST' => '',
        'HTTP_USER_AGENT' => '',
        'HTTP_ACCEPT' => '',
        'HTTP_ACCEPT_LANGUAGE' => '',
        'HTTP_ACCEPT_ENCODING' => '',
        'HTTP_COOKIE' => '',
        'HTTP_CONNECTION' => '',
        'REMOTE_ADDR' => '',
        'REMOTE_PORT' => '0',
    );

    // 将header分割成数组
    list($http_header, $http_body) = explode("\r\n\r\n", $http, 2);

    $header_data = explode("\r\n", $http_header);

    list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ', $header_data[0]);

    unset($header_data[0]);
    foreach($header_data as $content)
    {
        // \r\n\r\n
        if(empty($content))
        {
            continue;
        }
        list($key, $value) = explode(':', $content, 2);
        $key = strtolower($key);
        $value = trim($value);
        switch($key)
        {
            // HTTP_HOST
            case 'host':
                $_SERVER['HTTP_HOST'] = $value;
                $tmp = explode(':', $value);
                $_SERVER['SERVER_NAME'] = $tmp[0];
                if(isset($tmp[1]))
                {
                    $_SERVER['SERVER_PORT'] = $tmp[1];
                }
                break;
            // cookie
            case 'cookie':
                $_SERVER['HTTP_COOKIE'] = $value;
                //将字符串解析成多个变量
                parse_str(str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE);
                break;
            // user-agent
            case 'user-agent':
                $_SERVER['HTTP_USER_AGENT'] = $value;
                break;
            // accept
            case 'accept':
                $_SERVER['HTTP_ACCEPT'] = $value;
                break;
            // accept-language
            case 'accept-language':
                $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $value;
                break;
            // accept-encoding
            case 'accept-encoding':
                $_SERVER['HTTP_ACCEPT_ENCODING'] = $value;
                break;
            // connection
            case 'connection':
                $_SERVER['HTTP_CONNECTION'] = $value;
                break;
            case 'referer':
                $_SERVER['HTTP_REFERER'] = $value;
                break;
            case 'if-modified-since':
                $_SERVER['HTTP_IF_MODIFIED_SINCE'] = $value;
                break;
            case 'if-none-match':
                $_SERVER['HTTP_IF_NONE_MATCH'] = $value;
                break;
            case 'content-type':
                // 不同的请求类型：application/x-www-form-urlencoded application/json multipart/form-data text/xml
                //demo: Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA
                if(!preg_match('/boundary="?(\S+)"?/', $value, $match))
                {
                    $_SERVER['CONTENT_TYPE'] = $value;
                }
                else
                {
                    $_SERVER['CONTENT_TYPE'] = 'multipart/form-data';
                    $http_post_boundary = '--'.$match[1];
                }
                break;
        }
    }

    // 需要解析$_POST
    if($_SERVER['REQUEST_METHOD'] === 'POST')
    {
        //上传文件处理
        if(isset($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] === 'multipart/form-data')
        {

        }
        else
        {
            parse_str($http_body, $_POST);
            // $GLOBALS['HTTP_RAW_POST_DATA']
            $GLOBALS['HTTP_RAW_POST_DATA'] = $http_body;
        }
    }

    // QUERY_STRING
    $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
    if($_SERVER['QUERY_STRING'])
    {
        // $GET
        parse_str($_SERVER['QUERY_STRING'], $_GET);
    }
    else
    {
        $_SERVER['QUERY_STRING'] = '';
    }

    // REQUEST
    // $_REQUEST = array_merge($_GET, $_POST);

    return array($_GET, $_POST, $_COOKIE, $_SERVER);
}
